package container.map;

import lombok.AllArgsConstructor;

import java.util.*;

/**
 * https://blog.csdn.net/f641385712/article/details/81880711
 * <p>
 * IdentityHashMap 重写了 equals 和 hashcode方法，不过需要注意的是 hashCode 方法并不是借助 Object 的 hashCode 来实现的，而是
 * 通过 System.identityHashCode 方法来实现的。
 * hashCode 的生成是与 key 和 value 都有关系的，这就间接保证了 key 和 value 这对数据具备了唯一的 hash 值。同时通过重写 equals 方法，
 * 判定只有 key 值全等情况下才会认为 key 值相等。这就是 IdentityHashMap 与普通 HashMap 不同的关键所在。
 */
@AllArgsConstructor
public class T07_IdentityHashMap {

    int i;

    public static void main(String[] args) {
        demo1();
        demo2();

        T07_IdentityHashMap demo1 = new T07_IdentityHashMap(1);
        T07_IdentityHashMap demo2 = new T07_IdentityHashMap(1);
        System.out.println(demo1 == demo2); // false，== 比较的是地址值，而不是 HashCode
        System.out.println(demo1.hashCode()); // 1
        System.out.println(demo2.hashCode()); // 1
        System.out.println(System.identityHashCode(demo1)); // 998351292
        System.out.println(System.identityHashCode(demo2)); // 1684106402
    }

    /**
     * IdentityHashMap 允许相同的 key 保存进来
     * IdentityHashMap，比较 key 值，直接使用的是 ==，== 比较的是地址值
     * IdentityHashMap 允许 key 和 value 都为 null
     */
    public static void demo2() {
        Map<String, String> identityHashMap = new IdentityHashMap<>();
        identityHashMap.put(new String("a"), "1");
        identityHashMap.put(new String("a"), "2");
        identityHashMap.put(new String("a"), "3");
        System.out.println(identityHashMap.size()); // 3

        Map<T07_IdentityHashMap, String> identityHashMap2 = new IdentityHashMap<>();
        identityHashMap2.put(new T07_IdentityHashMap(1), "1");
        identityHashMap2.put(new T07_IdentityHashMap(1), "2");
        identityHashMap2.put(new T07_IdentityHashMap(1), "3");
        System.out.println(identityHashMap2.size()); // 3

        System.out.println(identityHashMap.get("a")); // null
        System.out.println(identityHashMap2.get(new T07_IdentityHashMap(1))); // null

        // 如果要使之线程安全
        Map<String, String> map = Collections.synchronizedMap(identityHashMap);
    }

    /**
     * 我们对 Map 都有一个通用认知：只要 key 相同，就不能重复往里面 put，但是你真的了解“相同”这两个字吗？看下面这个例子
     */
    public static void demo1() {
        Map<String, String> map = new HashMap<>();
        map.put("a", "1");
        map.put("a", "2");
        map.put("a", "3");
        System.out.println(map.size()); // 1

        Map<String, String> hashMap = new HashMap<>();
        hashMap.put(new String("a"), "1");
        hashMap.put(new String("a"), "2");
        hashMap.put(new String("a"), "3");
        System.out.println(hashMap.size()); // 1

        Map<Integer, String> hashMap2 = new HashMap<>();
        hashMap2.put(new Integer(200), "1");
        hashMap2.put(new Integer(200), "2");
        hashMap2.put(new Integer(200), "3");
        System.out.println(hashMap2.size()); // 1

        Map<T07_IdentityHashMap, String> hashMap3 = new HashMap<>();
        hashMap3.put(new T07_IdentityHashMap(1), "1");
        hashMap3.put(new T07_IdentityHashMap(1), "2");
        hashMap3.put(new T07_IdentityHashMap(1), "3");
        System.out.println(hashMap3.size()); // 3
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        T07_IdentityHashMap that = (T07_IdentityHashMap) o;
        return i == that.i;
    }

    @Override
    public int hashCode() {
        return Objects.hash(i);
    }
}
